import { Action, Thunk, thunk } from "easy-peasy";
import { propSetterAction, simpleReadArrayBuffer } from "../../utils";
import { addAssetToProject } from "../../database/indexed-db";
import {
  FileProcessingFailure,
  IProcessFilesInteraction,
  processFilesBase,
} from "./process-files";
import { IPytchAppModel } from "..";
import {
  AssetOperationContext,
  AssetOperationContextKey,
  assetOperationContextFromKey,
  unknownAssetOperationContext,
} from "../asset";

export function addAssetErrorMessageFromError(
  operationContext: AssetOperationContext,
  fileBasename: string,
  error: Error
) {
  if (error.name === "PytchDuplicateAssetNameError") {
    return (
      `Cannot add "${fileBasename}" to ${operationContext.scope}` +
      ` because it already contains ${operationContext.assetIndefinite}` +
      " of that name."
    );
  } else {
    return error.message;
  }
}

type AddAssetsLaunchArgs = {
  operationContextKey: AssetOperationContextKey;
  assetNamePrefix: string;
};

type MessageFromErrorArgs = {
  error: Error;
  fileBasename: string;
};

export type AddAssetsInteractionSpecific = {
  assetNamePrefix: string;
  setAssetNamePrefix: Action<AddAssetsInteraction, string>;
  operationContext: AssetOperationContext;
  setOperationContext: Action<AddAssetsInteraction, AssetOperationContext>;

  launchAdd: Thunk<AddAssetsInteraction, AddAssetsLaunchArgs>;

  _messageFromError: Thunk<
    AddAssetsInteraction,
    MessageFromErrorArgs,
    void,
    IPytchAppModel,
    string
  >;
};

export type AddAssetsInteraction =
  IProcessFilesInteraction<AddAssetsInteractionSpecific>;

export const addAssetsInteraction: AddAssetsInteraction = {
  ...processFilesBase(),

  assetNamePrefix: "",
  setAssetNamePrefix: propSetterAction("assetNamePrefix"),

  operationContext: unknownAssetOperationContext,
  setOperationContext: propSetterAction("operationContext"),

  launchAdd: thunk((actions, args) => {
    actions.setAssetNamePrefix(args.assetNamePrefix);

    const opContext = assetOperationContextFromKey(args.operationContextKey);
    actions.setOperationContext(opContext);

    actions.launch();
  }),

  _messageFromError: thunk((_actions, { error, fileBasename }, helpers) => {
    const opContext = helpers.getState().operationContext;
    return addAssetErrorMessageFromError(opContext, fileBasename, error);
  }),

  tryProcess: thunk(async (actions, files, helpers) => {
    // It's possible this will change while we're working, e.g., if the
    // user hits "back" and then opens a different project.  Make sure
    // we add all assets to the project which was live when the thunk
    // was launched.
    const projectId = helpers.getStoreState().activeProject.project.id;

    const assetNamePrefix = helpers.getState().assetNamePrefix;

    actions.setScalar("trying-to-process");

    let failedAdds: Array<FileProcessingFailure> = [];

    for (const file of files) {
      try {
        const fileBuffer = await simpleReadArrayBuffer(file);
        const assetPath = `${assetNamePrefix}${file.name}`;
        await addAssetToProject(projectId, assetPath, file.type, fileBuffer);
      } catch (
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        e: any
      ) {
        console.error("addAssetsInteraction.tryProcess():", e);
        const reason = actions._messageFromError({
          error: e,
          fileBasename: file.name,
        });
        failedAdds.push({ fileName: file.name, reason });
      }
    }

    // Check the active project now is the same one we worked with.
    const liveProjectId = helpers.getStoreState().activeProject.project.id;
    if (liveProjectId !== projectId) {
      console.log(
        `assets added to project ${projectId}` +
          ` but now active is project ${liveProjectId}; bailing`
      );
      return;
    }

    await helpers.getStoreActions().activeProject.syncAssetsFromStorage();

    if (failedAdds.length > 0) {
      actions.setFailed(failedAdds);
    } else {
      actions.setScalar("idle");
    }
  }),
};
